package br.net.woodstock.tickettoride.api.client.common;

import java.security.cert.X509Certificate;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;

import br.net.woodstock.rockframework.domain.service.ServiceException;
import br.net.woodstock.rockframework.security.cert.CertificateType;
import br.net.woodstock.rockframework.security.util.SecurityUtils;
import br.net.woodstock.tickettoride.api.client.TicketData;
import br.net.woodstock.tickettoride.api.client.TicketID;
import br.net.woodstock.tickettoride.api.client.TicketRequest;
import br.net.woodstock.tickettoride.api.client.TicketServiceWS;
import br.net.woodstock.tickettoride.api.client.TicketSession;
import br.net.woodstock.tickettoride.api.manager.ApplicationService;
import br.net.woodstock.tickettoride.api.manager.SessionService;
import br.net.woodstock.tickettoride.api.manager.TicketService;
import br.net.woodstock.tickettoride.client.utils.CodecUtils;
import br.net.woodstock.tickettoride.orm.Application;
import br.net.woodstock.tickettoride.orm.Certificate;
import br.net.woodstock.tickettoride.orm.Session;
import br.net.woodstock.tickettoride.orm.Ticket;

public abstract class AbstractTicketServiceWS implements TicketServiceWS {

	public abstract ApplicationService getApplicationService();

	public abstract SessionService getSessionService();

	public abstract TicketService getTicketService();

	@Override
	@WebMethod(action = "createSession", operationName = "createSession")
	@WebResult(name = "ticketSession")
	public TicketSession createSession(@WebParam(name = "application") final String application) {
		try {
			Application a = this.getApplicationService().getByAlias(application);
			if (a == null) {
				throw new ServiceException("Application not found");
			}

			Integer id = this.getSessionService().create(a.getId());
			Session s = this.getSessionService().getById(id);
			Certificate c = s.getCertificate();
			X509Certificate x509c = (X509Certificate) SecurityUtils.getCertificateFromFile(CodecUtils.fromBase64(c.getData()), CertificateType.X509);
			byte[] encoded = CodecUtils.encrypt(x509c.getPublicKey(), CodecUtils.fromBase64(s.getKey()));
			byte[] base64 = CodecUtils.toBase64(encoded);
			TicketSession session = new TicketSession(s.getHash(), base64);
			return session;
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}

	@Override
	@WebMethod(action = "createTicket", operationName = "createTicket")
	@WebResult(name = "ticketID")
	public TicketID createTicket(@WebParam(name = "request") final TicketRequest request) {
		try {
			Session s = this.getSessionService().getByHash(request.getSession().getHash());
			if (s == null) {
				throw new ServiceException("Session not found");
			}

			Integer[] applicationIds = new Integer[request.getToApplications().length];
			for (int i = 0; i < request.getToApplications().length; i++) {
				String alias = request.getToApplications()[i];
				Integer id = this.getApplicationService().getIdByAlias(alias);
				if (id != null) {
					applicationIds[i] = id;
				} else {
					throw new ServiceException("Application not found " + alias);
				}

			}

			Certificate c = s.getCertificate();
			X509Certificate x509c = (X509Certificate) SecurityUtils.getCertificateFromFile(CodecUtils.fromBase64(c.getData()), CertificateType.X509);
			byte[] encryptedSignature = CodecUtils.fromBase64(request.getSignature());
			byte[] encryptedData = CodecUtils.fromBase64(request.getData());
			byte[] decriptedSignature = CodecUtils.decrypt(x509c.getPublicKey(), encryptedSignature);
			String sha1Signature = CodecUtils.toString(decriptedSignature);
			String sha1 = CodecUtils.getSHA1String(encryptedData);

			if (!sha1Signature.equals(sha1)) {
				throw new ServiceException("Signature not match");
			}

			Integer id = this.getTicketService().create(s.getId(), applicationIds, request.getData(), request.getExpires());
			Ticket ticket = this.getTicketService().getById(id);
			TicketID ticketID = new TicketID(ticket.getHash());
			return ticketID;
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}

	@Override
	@WebMethod(action = "getTicketData", operationName = "getTicketData")
	@WebResult(name = "ticketData")
	public TicketData getTicketData(@WebParam(name = "session") final TicketSession session, @WebParam(name = "id") final TicketID id) {
		try {
			Session s = this.getSessionService().getByHash(session.getHash());
			if (s == null) {
				throw new ServiceException("Session not found");
			}

			Ticket t = this.getTicketService().getByHash(id.getHash());
			if (t == null) {
				throw new ServiceException("Ticket not found");
			}

			Certificate c = s.getCertificate();
			X509Certificate x509c = (X509Certificate) SecurityUtils.getCertificateFromFile(CodecUtils.fromBase64(c.getData()), CertificateType.X509);
			byte[] data = this.getTicketService().getEncryptedTicketData(s.getId(), t.getId());
			byte[] sha1 = CodecUtils.getSHA1(data);
			byte[] signature = CodecUtils.encrypt(x509c.getPublicKey(), sha1);
			byte[] base64Data = CodecUtils.toBase64(data);
			byte[] base64Signature = CodecUtils.toBase64(signature);
			TicketData ticketData = new TicketData(t.getSession().getCertificate().getApplication().getAlias(), t.getCreation(), t.getExpires(), base64Data, base64Signature);
			return ticketData;
		} catch (Exception e) {
			throw new ServiceException(e);
		}
	}
}
